﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading;
using Microsoft.Practices.Unity;
using Wyn.PMS.Common.SecurityControl;
using Wyn.PMS.Common.Utils;
using Wyn.PMS.Entity.Entities;
using Wyn.PMS.Entity.IDataAccess;

namespace Wyn.PMS.Common.NightAudit
{
    /// <summary>
    /// 房态缓存
    /// <para>--------------------------</para>
    /// <para>@Author: cnalexi@gmail.com</para> 
    /// <para>@Create: 2012/3/31</para>  
    /// </summary>
    public class RoomStatusCache
    {
        #region ☆ -------- 初始化做预备 -------- ☆

        private IDictionary<string, IDictionary<string, Room>> _cache =
            new Dictionary<string, IDictionary<string, Room>>();

        private IDictionary<string, DateTime> roomStatusTimeStampes = new Dictionary<string, DateTime>();

        private static volatile RoomStatusCache instance;

        private static readonly object lockThis = new object();

        private static readonly ReaderWriterLockSlim barrier = new ReaderWriterLockSlim();
        
        private readonly DateTime InitTimeStamp = DateTime.Parse("1984/11/09 18:00:00 PM");

        private DateTime versionTimeStamp = DateTime.Parse("1984/11/09 18:00:00 PM");

        private static readonly IUnityContainer container = UnityContainerService.GetContainer();

        private ReadonlyDictionary<string, IDictionary<string, Room>> _readonlyRoomStatus;

        private string _existCodes = string.Empty;

        public DateTime VersionTimeStamp
        {
            get { return versionTimeStamp; }
            set { versionTimeStamp = value; }
        }

        private RoomStatusCache()
        {
        }

        public static RoomStatusCache GetInstance()
        {
            if(instance==null)
            {
                lock(lockThis)
                {
                    if(instance==null)
                    {
                        var rsc = new RoomStatusCache();

                        rsc.InitRoomStatus();

                        instance = rsc;

                        Thread.MemoryBarrier();
                    }
                }
            }
            return instance;
        }

        #endregion

        #region ☆ -------- 内部处理逻辑 -------- ☆

        public void InitRoomStatus()
        {
            //System.Diagnostics.Debug.WriteLine("初始化房态:" + DateTime.Now.ToString(CultureInfo.InvariantCulture));

            var RoomRepository = container.Resolve<IRoomRepository>();

            try
            {
                IList<string> hotelIds = BizDay.GetAll().Select(h => h.HotelID).ToList();
                
                foreach(var hotelId in hotelIds)
                {
                    string id = hotelId;

                    Expression<Func<Room, bool>> exp = r => r.HotelID==id && r.ModifiedTime >= InitTimeStamp;

                    IList<Room> rooms = null;

                    var transactionOptions = new System.Transactions.TransactionOptions();

                    transactionOptions.IsolationLevel = System.Transactions.IsolationLevel.ReadUncommitted;

                    using (var scope = new System.Transactions.TransactionScope(System.Transactions.TransactionScopeOption.Required, transactionOptions))
                    {
                        rooms = RoomRepository.BatchIncludeFind(GlobalConst.ROOT_HOTEL_ID, exp).ToList();

                        scope.Complete();
                    }

                    if ( rooms.Count>0) FormatCacheItems(rooms);
                }
            }
            catch (Exception error)
            {
                OperateLog.ErrorLog(DateTime.Now + "房态缓存刷新失败" + error);
            }

        }

        public void Refresh(DateTime? reloadTime)
        {
            try
            {
                //测试监视器
                //System.Diagnostics.Debug.WriteLine("重刷房态:"+DateTime.Now.ToString(CultureInfo.InvariantCulture));
                DateTime time = reloadTime ?? versionTimeStamp;

                var RoomRepository = container.Resolve<IRoomRepository>();

                Expression<Func<Room, bool>> exp = r => r.ModifiedTime > time;

                IList<Room> rooms = null;

                var transactionOptions = new System.Transactions.TransactionOptions();

                transactionOptions.IsolationLevel = System.Transactions.IsolationLevel.ReadUncommitted;

                using (var scope = new System.Transactions.TransactionScope(System.Transactions.TransactionScopeOption.Required, transactionOptions))
                {
                    rooms= RoomRepository.BatchIncludeFind(GlobalConst.ROOT_HOTEL_ID, exp).ToList();

                    scope.Complete();
                }

                if (rooms.Count > 0) FormatCacheItems(rooms);
                
            }
            catch (Exception error)
            {
                OperateLog.ErrorLog(DateTime.Now + "房态缓存刷新失败" + error);
            }
        }
        
        private void FormatCacheItems(IList<Room> rooms)
        {
            if (rooms == null || !rooms.Any()) return;

            VersionTimeStamp = rooms.Max(r => r.ModifiedTime);
            
            foreach(var room in rooms)
            {
                barrier.EnterWriteLock();

                if(!_cache.ContainsKey(room.HotelID))
                {
                    CreateHotelCache(room.HotelID);
                }

                IDictionary<string, Room> list = _cache[room.HotelID];

                if(!list.ContainsKey(room.RoomNo))
                {
                    list.Add(room.RoomNo,new Room());
                }

                Room cacheRoom = list[room.RoomNo];

                CloneRoom(cacheRoom, room);

                if(roomStatusTimeStampes[room.HotelID]<room.ModifiedTime)
                {
                    roomStatusTimeStampes[room.HotelID] = room.ModifiedTime;
                }

                barrier.ExitWriteLock();
            }

            WrapReadonlyRoomStatus();

        }

        private void CreateHotelCache(string hotelId)
        {
            IDictionary<string, Room> statusContainer = new Dictionary<string, Room>();

            _cache.Add(hotelId, statusContainer);

            if(!roomStatusTimeStampes.ContainsKey(hotelId))
            {
                roomStatusTimeStampes.Add(hotelId,InitTimeStamp);
            }
        }
        
        private void CloneRoom(Room oldRoom, Room newRoom)
        {
            oldRoom.HotelID = newRoom.HotelID;

            oldRoom.RoomNo = newRoom.RoomNo;

            oldRoom.RoomName = newRoom.RoomName;

            oldRoom.RoomAttribute = newRoom.RoomAttribute;

            oldRoom.RoomDescript = newRoom.RoomDescript;

            oldRoom.RoomCardNo = newRoom.RoomCardNo;

            oldRoom.RoomTypeID = newRoom.RoomTypeID;

            oldRoom.OrderID = newRoom.OrderID;

            oldRoom.ModifiedTime = newRoom.ModifiedTime;

            oldRoom.Memo = newRoom.Memo;

            oldRoom.IsValid = newRoom.IsValid;

            oldRoom.Status = newRoom.Status;

            oldRoom.TeleNo = newRoom.TeleNo;

            oldRoom.BuildingID = newRoom.BuildingID;

            oldRoom.EmployeeID = newRoom.EmployeeID;

            oldRoom.FloorID = newRoom.FloorID;

            oldRoom.GuestName = newRoom.GuestName;


        }
        
        #endregion

        #region ☆ -------- 外部访问方法 -------- ☆

        public IEnumerable<Room> GetCacheRooms(DateTime lastRefreshTime, string hotelId)
        {
            if (!_cache.ContainsKey(hotelId))
            {
                throw new Exception("房态图中不存在此酒店的房态,酒店编号" + hotelId);
            }

            IDictionary<string, Room> roomDict = RoomStatus[hotelId];

            #region
            /*有可能用LINQ无法访问ReadOnly dictionary的情况
             foreach(var room in roomDict.Values)
            {
                if(room.ModifiedTime>lastRefreshTime)
                {
                    rooms.Add(room);
                }
            }
            */
            #endregion

            return roomDict.Values.Where(room => room.ModifiedTime > lastRefreshTime).ToList();

        }

        public IEnumerable<Room> GetCacheRooms(DateTime lastRefreshTime, string hotelId,string roomTypeId)
        {
            if (!_cache.ContainsKey(hotelId))
            {
                throw new Exception("房态图中不存在此酒店的房态,酒店编号" + hotelId);
            }

            IDictionary<string, Room> roomDict = RoomStatus[hotelId];

            return roomDict.Values.Where(room => room.ModifiedTime > lastRefreshTime && room.RoomTypeID==roomTypeId && room.IsValid).ToList();

        }

        public DateTime GetLastModifiedTime(string hotelId)
        {
            if(!roomStatusTimeStampes.ContainsKey(hotelId))
            {
                throw new Exception("该酒店房态不存在，请确认是否已经初始化,酒店编号:"+hotelId);
            }

            return roomStatusTimeStampes[hotelId];

        }

        public Room GetRoom(string hotelId, string roomNo)
        {
            if (!_cache.ContainsKey(hotelId))
            {
                throw new Exception("房态图中不存在此酒店的房态,酒店编号" + hotelId);
            }

            IDictionary<string, Room> roomDict = RoomStatus[hotelId];

            return roomDict.Values.FirstOrDefault(room => room.RoomNo ==roomNo );
        }

        /// <summary>
        /// 
        /// </summary>
        public IDictionary<string, IDictionary<string, Room>> RoomStatus
        {
            get { return _readonlyRoomStatus; }
        }


        /// <summary>
        /// 打包房态
        /// </summary>
        private void WrapReadonlyRoomStatus()
        {
            var dict = new Dictionary<string, IDictionary<string, Room>>();

            foreach (string key in _cache.Keys)
            {
                var subDict = new ReadonlyDictionary<string, Room>(_cache[key]);

                dict.Add(key, subDict);
            }

            var readonlyRoomStatus = new ReadonlyDictionary<string, IDictionary<string, Room>>(dict);

            _readonlyRoomStatus = readonlyRoomStatus;

        }
        
        #endregion

        
        
    }
}
